#-----------------------------------------------------------------------------
# Name:         pysrcds.srcds
# Purpose:     The actual SRCDS emulator
#
# Author:      Thomas Morton
#
# Created:     03/05/2008
# Copyright:   (c) 2008 Thomas Morton
#-----------------------------------------------------------------------------

'''

This is the meat of PySrcds

It provides the basic server class and associated callbacks, hooks and methods.

'''

# imports
# es-ide specific
from utilities import errors,logging,Environment
from third_party.path import path
# general
import threading
import time

# error classes
class EventError(errors.Error):
    pass
    
class ServerError(errors.Error):
    pass

class CvarError(errors.Error):
    # MUST not exit the server..
    pass
    
# this is the ultimate nut's and bolts of PySrcds
class _SourceServer(threading.Thread):
    '''
        Implements a source server emulation
    '''
    def __init__(self,name):
        '''
            Setup the server, contains some generic stuff
        '''
        # does the directory exist yet?
        if not self.dir.exists():
            # make it
            self.dir.mkdir(1)
            # copy the default directory structure
            self._copy_default()
        else:
            # the directory is there so lets check it's structure and load from file
            # first create the defaults
            self._load_defaults()
            # then overwrite by loading the data
            self._load_from_directory()
            
        # imit soem variables
        self.commandlist = {}  
        self.cvarlist = {}  
        
    # some methods that need writing - but are here to raise sensible errors
    def _copy_default(self):
        raise errors.NotImplemented("pysrcds.srcds.__SourceServer._copy_default")
    def _load_from_directory(self):
        raise errors.NotImplemented("pysrcds.srcds.__SourceServer._load_from_directory")
    def _parse_cfg(self,file):
        raise errors.NotImplemented("pysrcds.srcds.__SourceServer._parse_cfg")
    def _load_defaults(self,file):
        raise errors.NotImplemented("pysrcds.srcds.__SourceServer._load_defaults")
        
    # internal-ly type stuff
    def _record(self,lvl,action):
        '''
            Record whats happens inside the server
        '''
        # record the action
        self.ServerHistory.append({"timestamp":int(time.time()),"action":action})
        # log the action
        logging.log(lvl,"Server (%s): %s" % (self.name,action))
    
    # this one parses a cfg file (the normal srcds cfg style) when passed a path() instance
    def parse_cfg(self,file):
        '''
            Runs through a cfg file and executes the commands / sets the cvars
        '''
        # iterate through the file
        for line in file.lines():
            # get run of newlines
            line=line.strip("\n")
            # continue if blank
            if line.startswith("\\"): continue
            # split by spaces and get rid of empty elements
            elements = []
            for element in line.split(" "):
                if element != "":
                    elements.append(element)
            # ignore blank lines
            if len(elements) < 1: continue
            # check if it is a command
            cmd = self.command(elements[0])
            if cmd:
                cmd.fire(elements)
            # nope not a command
            else:
                # try setting a cvar
                # gotta have 2 elements for a proper cvar
                # should probably add an error here...
                if len(elements) < 2: continue
                # ok w'ere good so lets set a cvar!
                self.cvar(elements[0],elements[1])
                
    # stuff related to server commands
    def command(self,name,callback=None):
        pass
     
    class Command(object):
        def fire(self,argv):
            '''
                Fires the command
                    argv is an iterator with the arguments in it
            '''
            pass
    # stuff related to server Cvars
    def cvar(self,name,value=None):
        # if we are just after the cvar then return the class instance
        if not value:
            # if it exists!
            if name in self.cvarlist:
                # log what we did (lvl 11 = really high up!)
                self._record(11,"Cvar %s instance requested" % name)
                # return the cvar instance finally
                return self.cvarlist[name]
        # well it wasn't there.. or we are meant to be creating the cvar
        # check that it is not a command first
        if name in self.commandlist:
            raise ServerError("Unable to create cvar %s, it already exists as a command")
        # so then! lets create the class instance
        temp = Cvar(name,value)
        # add it to the internal dictionary
        self.cvarlist[name] = temp
        # log what we did
        self._record(9,"Created Cvar %s" % name)
        # return it!
        return temp
        
    class Cvar(object):
        '''
         Internal class implementing an individual cva
            Adds important loggingand history info as well as the basic Cvar behaviour
        '''
        def __init__(self,name,value=None):
            # initialise variables
            self.name=name
            self.CvarHistory = []
            self.flags=={"public":0}
            # if a value was set initially then set it as such
            if value:
                self.set(value)
            # else set it to **empty**
            else:
                self.set("**empty**")
            # record what happened
            self._record("Created cvar")
            
        def set(self,value):
            # what are we doing?
            action = "Value change from %s to %s" % (self.value,value)
            # record it
            self._record(action)
            # set the cvar to a value
            self.value = value
            
        def makepublic(self):
            # what are we doing?
            action = "Made public"
            # record it
            self._record(action)
            # make it public
            self.flags["public"] = 1
            
        def _record(self,action):
            # record all the actions onm the cvar and log them at a huge level
            # record the action
            self.CvarHistory.append({"timestamp":int(time.time()),"action":action})
            # log the action
            logging.log(12,"Cvar (%s): %s" % (self.name,action))
            
    def run():
        '''
            Starts the server safely
        '''
        self._start_server()
        
    def __start_server(self):
        '''
            Actually start the server off
        '''
        pass
    def isEvent(self,event):
        '''
            Check if a named event exists
        '''
        if event in self.events:
            return True
        return False
        
    def registerEvent(self,event,method,callback=None,event_data=[]):
        '''
            * Name: the all important event name
            * Method: the method called when the event is fired, bascially runs the event
                        it is passed an instance of this server (self) and the evewnt instance
            * Callback: a method called after the event is run and passed the event data
        '''
        # check this event is already registered
        if event in self.events:
            raise EventError("%s is already registered as an event")
            return
        # register the event
        self.events[event] = __ServerEvent(name,method)
        # sort out the callbacks
        if callback:
            self.events[event]._add_callback(callback)
        # and the event data
        for x in event_data:
            self.events[event]._add_return(x)
            
    def eventCallback(self,event,callback):
        '''
            Register a callback for an event
        '''
        # check this event is registered
        if event not in self.events:
            raise EventError("%s does not exist")
            return
        self.events[event]._add_callback(callback)
        
    def fireEvent(self,event):
        '''
            Fires a registered event
        '''
        # check this event is registered
        if event not in self.events:
            raise EventError("%s does not exist")
            return
        self.events[event]._fire()
        
class __ServerEvent(object):
    '''
        Events are objects - makes it nice and easy
    '''
    def __init__(self,name,method):
        self.name=name
        self.method=method
        self.callbacks = []
        self.return_data = {}
        
    # external methods  
    def _add_return(self,name):
        self.return_data[name]=""
        
    def _add_callback(self,callback):
        self.callbacks.append(callback)
        
    def _fire(self,server):
        '''
            Fires the event
        '''
        # fire the event method and pass it the server and this event class
        self.method(self,server)
        # now fire the callbacks and pass the return data
        for x in self.callbacks:
            x(self.return_data)
            
# utility methods
        